﻿using System.Text.RegularExpressions;

namespace System;

public static class StringExtension
{
    /// <summary>
    ///     A string extension method that query if '@this' is null or empty.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <returns>true if null or empty, false if not.</returns>
    public static bool IsNullOrEmpty(this string? @this) => string.IsNullOrEmpty(@this);

    /// <summary>
    ///     A string extension method that query if '@this' is not null and not empty.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <returns>false if null or empty, true if not.</returns> 
    public static bool IsNotNullOrEmpty(this string? @this) => !string.IsNullOrEmpty(@this);

    /// <summary>
    ///     A string extension method that query if '@this' is null or whiteSpace.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <returns>true if null or whiteSpace, false if not.</returns>
    public static bool IsNullOrWhiteSpace(this string? @this) => string.IsNullOrWhiteSpace(@this);

    /// <summary>
    ///     A string extension method that query if '@this' is not null and not whiteSpace.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <returns>false if null or whiteSpace, true if not.</returns>
    public static bool IsNotNullOrWhiteSpace(this string? @this) => !string.IsNullOrWhiteSpace(@this);

    /// <summary>
    ///     A string extension method that query if '@this' satisfy the specified pattern.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <param name="pattern">The pattern to use. Use '*' as wildcard string.</param>
    /// <returns>true if '@this' satisfy the specified pattern, false if not.</returns>
    public static bool IsLike([NotNull] this string @this, string pattern)
    {
        // Turn the pattern into regex pattern, and match the whole string with ^$
        var regexPattern = "^" + Regex.Escape(pattern) + "$";

        // Escape special character ?, #, *, [], and [!]
        regexPattern = regexPattern.Replace(@"\[!", "[^")
            .Replace(@"\[", "[")
            .Replace(@"\]", "]")
            .Replace(@"\?", ".")
            .Replace(@"\*", ".*")
            .Replace(@"\#", @"\d");

        return Regex.IsMatch(@this, regexPattern);
    }

    /// <summary>
    ///     A string extension method that repeats the string a specified number of times.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <param name="repeatCount">Number of repeats.</param>
    /// <returns>The repeated string.</returns>
    public static string Repeat([NotNull] this string @this, int repeatCount)
    {
        if (@this.Length == 1)
        {
            return new string(@this[0], repeatCount);
        }

        var sb = new StringBuilder(repeatCount * @this.Length);
        while (repeatCount-- > 0)
        {
            sb.Append(@this);
        }

        return sb.ToString();
    }

    /// <summary>
    ///     A string extension method that reverses the given string.
    /// </summary>
    /// <param name="this">The @this to act on.</param>
    /// <returns>The string reversed.</returns>
    public static string Reverse([NotNull] this string @this)
    {
        if (@this.Length <= 1)
        {
            return @this;
        }

        var chars = @this.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }

    public static byte[] GetBytes([NotNull] this string str, Encoding encoding) => encoding.GetBytes(str);

    /// <summary>
    ///     A string extension method that converts the @this to an enum.
    /// </summary>
    /// <typeparam name="T">Generic type parameter.</typeparam>
    /// <param name="this">The @this to act on.</param>
    /// <returns>@this as a T.</returns>
    public static T ToEnum<T>([NotNull] this string @this) => (T)Enum.Parse(typeof(T), @this);

    /// <summary>
    /// EqualsIgnoreCase
    /// </summary>
    /// <param name="s1">string1</param>
    /// <param name="s2">string2</param>
    /// <returns></returns>
    public static bool EqualsIgnoreCase(this string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);

    /// <summary>
    /// string=>long
    /// </summary>
    /// <param name="txt"></param>
    /// <returns></returns>
    public static long? ToLong(this string @this)
    {
        bool status = long.TryParse(@this, out long result);

        if (status)
            return result;
        else
            return null;
    }

    /// <summary>
    /// hex string to byte extension
    /// </summary>
    /// <param name="hex"></param>
    /// <returns></returns>
    public static byte[] ToBytes(this string hex)
    {
        if (hex.Length == 0)
        {
            return new byte[] { 0 };
        }
        if (hex.Length % 2 == 1)
        {
            hex = "0" + hex;
        }
        byte[] result = new byte[hex.Length / 2];
        for (int i = 0; i < hex.Length / 2; i++)
        {
            result[i] = byte.Parse(hex.Substring(2 * i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
        }
        return result;
    }

    /// <summary>确保字符串不超过指定的长度</summary>
    public static string EnsureLength(this string @this, int length)
    {
        if (string.IsNullOrEmpty(@this)) return @this;
        if (@this.Length <= length) return @this;

        return @this[0..length];
    }

    /// <summary>确保字符串以指定的另一字符串结束，不区分大小写</summary>
    /// <param name="@this">字符串</param>
    /// <param name="end">结束字符串</param>
    /// <returns></returns>
    public static string EnsureEnd(this string @this, string end)
    {
        if (string.IsNullOrEmpty(end)) return @this;
        if (string.IsNullOrEmpty(@this)) return end;

        if (@this.EndsWith(end, StringComparison.OrdinalIgnoreCase)) return @this;

        return @this + end;
    }

    /// <summary>从当前字符串开头移除另一字符串，不区分大小写，循环多次匹配前缀</summary>
    /// <param name="str">当前字符串</param>
    /// <param name="starts">另一字符串</param>
    /// <returns></returns>
    public static string TrimStart(this string @this, params string[] starts)
    {
        if (string.IsNullOrEmpty(@this) ||
            starts == null || starts.Length < 1)
        {
            return @this;
        }

        for (var i = 0; i < starts.Length; i++)
        {
            if (string.IsNullOrEmpty(starts[i])) continue;
            if (@this.StartsWith(starts[i], StringComparison.OrdinalIgnoreCase))
            {
                @this = @this.Substring(starts[i].Length);
                if (string.IsNullOrEmpty(@this)) break;

                // 从头开始
                i = -1;
            }
        }
        return @this;
    }

    /// <summary>从当前字符串结尾移除另一字符串，不区分大小写，循环多次匹配后缀</summary>
    /// <param name="str">当前字符串</param>
    /// <param name="ends">另一字符串</param>
    /// <returns></returns>
    public static string TrimEnd(this string str, params string[] ends)
    {
        if (string.IsNullOrEmpty(str)) return str;
        if (ends == null || ends.Length < 1 || string.IsNullOrEmpty(ends[0])) return str;

        for (var i = 0; i < ends.Length; i++)
        {
            if (str.EndsWith(ends[i], StringComparison.OrdinalIgnoreCase))
            {
                str = str.Substring(0, str.Length - ends[i].Length);
                if (string.IsNullOrEmpty(str)) break;

                // 从头开始
                i = -1;
            }
        }
        return str;
    }

    public static string ToLog(this string @this, string logValue, bool isNewLine = false)
    {
        if (string.IsNullOrWhiteSpace(logValue)) return string.Empty;

        return $"{(isNewLine ? "<br/>" : "")}{@this}：{logValue}";
    }

    public static string ToLogAppend(this string @this, string logValue)
        => string.IsNullOrWhiteSpace(logValue) ? @this : string.IsNullOrWhiteSpace(@this) ? logValue : $"{@this}：{logValue}";

    public static string ToLogAppendAndNull(this string @this, string logValue, bool isNewLine = false)
    => $"{(isNewLine ? "<br/>" : "")}{@this}：{(string.IsNullOrWhiteSpace(logValue) ? "(空)" : logValue)}";

    public static string ToLog(this string @this, string oldValue, string newValue, bool isNewLine = false)
    {
        if (string.Equals(oldValue, newValue) ||
            (string.IsNullOrWhiteSpace(oldValue) && string.IsNullOrWhiteSpace(newValue)))
        {
            return string.Empty;
        }

        var strOld = string.IsNullOrWhiteSpace(oldValue) ? "(空)" : $"【{oldValue}】";
        var strNew = string.IsNullOrWhiteSpace(newValue) ? "(空)" : $"【{newValue}】";
        return $"{(isNewLine ? "<br/>" : "")}{@this}{strOld}改为{strNew}";
    }

    public static string ToLog(this string @this, long oldValue, long newValue, bool isNewLine = false)
    => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue}】改为【{newValue}】";

    public static string ToLog(this string @this, int oldValue, int newValue, bool isNewLine = false)
    => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue}】改为【{newValue}】";

    public static string ToLog(this string @this, decimal oldValue, decimal newValue, bool isNewLine = false)
    => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue.TrimEndZero()}】改为【{newValue.TrimEndZero()}】";

    public static string ToLog(this string @this, DateTime oldValue, DateTime newValue, bool isNewLine = false)
    => oldValue == newValue ? string.Empty : $"{(isNewLine ? "<br/>" : "")}{@this}【{oldValue.ToStandardTimeString()}】改为【{newValue.ToStandardTimeString()}】";


    public static string ToLog(this string @this, long? oldValue, long? newValue, bool isNewLine = false, string defaultValue = "0")
    => @this.ToLog(oldValue.HasValue ? $"{oldValue.Value}" : defaultValue, newValue.HasValue ? $"{newValue.Value}" : defaultValue, isNewLine);

    public static string ToLog(this string @this, int? oldValue, int? newValue, bool isNewLine = false, string defaultValue = "0")
    => @this.ToLog(oldValue.HasValue ? $"{oldValue.Value}" : defaultValue, newValue.HasValue ? $"{newValue.Value}" : defaultValue, isNewLine);

    public static string ToLog(this string @this, decimal? oldValue, decimal? newValue, bool isNewLine = false, string defaultValue = "0")
    => @this.ToLog(oldValue.HasValue ? oldValue.Value.TrimEndZero() : defaultValue, newValue.HasValue ? newValue.Value.TrimEndZero() : defaultValue, isNewLine);

    public static string ToLog(this string @this, DateTime? oldValue, DateTime? newValue, bool isNewLine = false)
    => @this.ToLog(oldValue.HasValue ? oldValue.Value.ToStandardTimeString() : string.Empty, newValue.HasValue ? newValue.Value.ToStandardTimeString() : string.Empty, isNewLine);

    public static string ReplaceAppoint(this string @this, int length = 4, char replaceSymbol = '*')
    {
        if (@this.IsNullOrEmpty()) return @this;

        if (@this.Length == 1)
        {
            return @this.PadRight(length, replaceSymbol);
        }
        if (@this.Length > 1 && @this.Length <= length)
        {
            return @this[..1].PadRight(length, replaceSymbol) + @this[1..1];
        }
        return @this[..length].PadRight(length * 2, replaceSymbol) + @this[(length - 1)..length];
    }
}